package com.study.sbsummary.utils;

import com.google.common.base.Strings;
import lombok.extern.slf4j.Slf4j;

import javax.servlet.http.HttpServletRequest;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.regex.Pattern;

@Slf4j
public final class IpUtil {
    private static final Pattern ipPattern = Pattern.compile("\\b((?!\\d\\d\\d)\\d+|1\\d\\d|2[0-4]\\d|25[0-5])\\.((?!\\d\\d\\d)\\d+|1\\d\\d|2[0-4]\\d|25[0-5])\\.((?!\\d\\d\\d)\\d+|1\\d\\d|2[0-4]\\d|25[0-5])\\.((?!\\d\\d\\d)\\d+|1\\d\\d|2[0-4]\\d|25[0-5])\\b");

    private IpUtil() {
    }

    public static boolean verifyIp(String ip) {
        return !Strings.isNullOrEmpty(ip) && ipPattern.matcher(ip).matches();
    }

    public static long ipToLong(String ip) {
        long[] array = new long[4];
        int position1 = ip.indexOf(".");
        int position2 = ip.indexOf(".", position1 + 1);
        int position3 = ip.indexOf(".", position2 + 1);
        array[0] = Long.parseLong(ip.substring(0, position1));
        array[1] = Long.parseLong(ip.substring(position1 + 1, position2));
        array[2] = Long.parseLong(ip.substring(position2 + 1, position3));
        array[3] = Long.parseLong(ip.substring(position3 + 1));
        return (array[0] << 24) + (array[1] << 16) + (array[2] << 8) + array[3];
    }

    public static String ipToString(long ip) {
        StringBuilder sb = new StringBuilder(10);
        sb.append(String.valueOf(ip >>> 24));
        sb.append(".");
        sb.append(String.valueOf((ip & 16777215L) >>> 16));
        sb.append(".");
        sb.append(String.valueOf((ip & 65535L) >>> 8));
        sb.append(".");
        sb.append(String.valueOf(ip & 255L));
        return sb.toString();
    }

    public static String getAllIp(HttpServletRequest request) {
        String xforwardedFor = request.getHeader("x-forwarded-for");
        return Strings.isNullOrEmpty(xforwardedFor) ? getRemoteAddr(request) : xforwardedFor + "," + getRemoteAddr(request);
    }

    public static String getHttpClientIp(HttpServletRequest request) {
        String[] ips = getXForwardedFor(request);
        return ips != null && ips.length != 0 ? ips[0] : request.getRemoteAddr();
    }

    public static String getTcpClientIp(HttpServletRequest request) {
        String[] ips = getXForwardedFor(request);
        return ips != null && ips.length != 0 ? ips[ips.length - 1] : request.getRemoteAddr();
    }

    public static String[] getXForwardedFor(HttpServletRequest request) {
        if (request == null) {
            return null;
        } else {
            String xforwardedFor = request.getHeader("x-forwarded-for");
            return Strings.isNullOrEmpty(xforwardedFor) ? null : xforwardedFor.split(",");
        }
    }

    public static String getRemoteAddr(HttpServletRequest request) {
        return request == null ? null : request.getRemoteAddr();
    }

    public static String getLocalIp() {
        try {
            List<Inet4Address> addresses = getLocalIp4AddressFromNetworkInterface();
            if (!(addresses == null || addresses.isEmpty())) {
                InetAddress inetAddress = addresses.get(0);
                return inetAddress.getHostAddress();
            }
        } catch (SocketException e) {
            log.warn(e.getMessage());
        }
        return null;
    }

    public static List<Inet4Address> getLocalIp4AddressFromNetworkInterface() throws SocketException {
        List<Inet4Address> addresses = new ArrayList<>(1);
        Enumeration e = NetworkInterface.getNetworkInterfaces();
        if (e == null) {
            return addresses;
        }
        while (e.hasMoreElements()) {
            NetworkInterface n = (NetworkInterface) e.nextElement();
            if (!isValidInterface(n)) {
                continue;
            }
            Enumeration ee = n.getInetAddresses();
            while (ee.hasMoreElements()) {
                InetAddress i = (InetAddress) ee.nextElement();
                if (isValidAddress(i)) {
                    addresses.add((Inet4Address) i);
                }
            }
        }
        return addresses;
    }

    /**
     * 过滤回环网卡、点对点网卡、非活动网卡、虚拟网卡并要求网卡名字是eth或ens开头
     *
     * @param ni 网卡
     * @return 如果满足要求则true，否则false
     */
    private static boolean isValidInterface(NetworkInterface ni) throws SocketException {
        return !ni.isLoopback() && !ni.isPointToPoint() && ni.isUp() && !ni.isVirtual()
                && (ni.getName().startsWith("eth") || ni.getName().startsWith("ens"));
    }

    /**
     * 判断是否是IPv4，并且内网地址并过滤回环地址.
     */
    private static boolean isValidAddress(InetAddress address) {
        return address instanceof Inet4Address && address.isSiteLocalAddress() && !address.isLoopbackAddress();
    }
}
